﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

using ProjectBase.Core;
using ProjectBase.Core.Tools.Convert;

namespace ProjectBase.Tools.Converter
{
    public class NumberToWordConverter : INumberToWordConverter
    {
        public enum NumberType { Ten, Hundred, Thousand, Million, Billion, }
        public virtual string ConvertToWord(long number, string separator = null)
        {
            Separator = separator;
            return Process(number).Trim();
        }

        protected virtual string Process(long number)
        {
            long millions;
            long thousands;
            long hundreds;
            var billions = Math.DivRem(number, 1000000000, out millions);
            millions = Math.DivRem(millions, 1000000, out thousands);
            thousands = Math.DivRem(thousands, 1000, out hundreds);
            var result = ProcessBillions((int)billions)
                 + ProcessMillions((int)millions)
                 + ProcessThousands((int)thousands)
                 + ProcessHundreds((int)hundreds);
            return result;
        }

        protected virtual string ProcessBillions(int number)
        {
            if (number == 0)
            {
                return string.Empty;
            }
            var result = ProcessHundreds(number, NumberType.Billion)
                + GetBillions(number);
            return result;
        }
        protected virtual string ProcessMillions(int number)
        {
            if (number == 0)
            {
                return string.Empty;
            }
            var result = ProcessHundreds(number, NumberType.Million)
                + GetMillions(number);
            return result;
        }
        protected virtual string ProcessThousands(int number)
        {
            if (number == 0)
            {
                return string.Empty;
            }
            var result = ProcessHundreds(number, NumberType.Thousand)
                         + GetThousands(number);
            return result;
        }
        protected virtual string ProcessHundreds(int number, NumberType? numberType = null)
        {
            int tens;
            var thousands = Math.DivRem(number, 100, out tens);
            var result = GetHundreds(thousands)
                   + GetTens(tens, numberType);
            return result;
        }
        protected virtual string GetOnes(int count, NumberType? numberType = null)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 9);
            var separator = numberType.HasValue && numberType.Equals(NumberType.Ten)
                    ? "-"
                    : Separator;

            switch (count)
            {
                case 1:
                    return separator + "one";
                case 2:
                    return separator + "two";
                case 3:
                    return separator + "three";
                case 4:
                    return separator + "four";
                case 5:
                    return separator + "five";
                case 6:
                    return separator + "six";
                case 7:
                    return separator + "seven";
                case 8:
                    return separator + "eight";
                case 9:
                    return separator + "nine";
            }
            return string.Empty;
        }
        protected virtual string GetTens(int count, NumberType? numberType = null)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 99);
            if (count <= 9)
            {
                return GetOnes(count, numberType);
            }
            switch (count)
            {
                case 10:
                    return Separator + "ten";
                case 11:
                    return Separator + "eleven";
                case 12:
                    return Separator + "twelve";
                case 13:
                    return Separator + "thirteen";
                case 14:
                    return Separator + "fourteen";
                case 15:
                    return Separator + "fifteen";
                case 16:
                    return Separator + "sixteen";
                case 17:
                    return Separator + "seventeen";
                case 18:
                    return Separator + "eighteen";
                case 19:
                    return Separator + "nineteen";
            }

            int remainder;
            var result = Math.DivRem(count, 10, out remainder);


            switch (result)
            {
                case 2:
                    return Separator + "twenty" + GetOnes(remainder, NumberType.Ten);
                case 3:
                    return Separator + "thirty" + GetOnes(remainder, NumberType.Ten);
                case 4:
                    return Separator + "forty" + GetOnes(remainder, NumberType.Ten);
                case 5:
                    return Separator + "fifty" + GetOnes(remainder, NumberType.Ten);
                case 6:
                    return Separator + "sixty" + GetOnes(remainder, NumberType.Ten);
                case 7:
                    return Separator + "seventy" + GetOnes(remainder, NumberType.Ten);
                case 8:
                    return Separator + "eighty" + GetOnes(remainder, NumberType.Ten);
                case 9:
                    return Separator + "ninety" + GetOnes(remainder, NumberType.Ten);
            }
            return string.Empty;
        }
        protected virtual string GetHundreds(int count)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 9);
            if (count == 0)
            {
                return string.Empty;
            }

            return GetOnes(count, NumberType.Hundred) + Separator + "hundred";
        }
        protected virtual string GetBillions(int count)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 999);
            if (count == 0)
            {
                return string.Empty;
            }

            return Separator + "billion";
        }
        protected virtual string GetMillions(int count)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 999);
            if (count == 0)
            {
                return string.Empty;
            }

            return Separator + "million";
        }
        protected virtual string GetThousands(int count)
        {
            Contract.Requires(count >= 0);
            Contract.Requires(count <= 999);
            if (count == 0)
            {
                return string.Empty;
            }

            return Separator + "thousand";
        }

        string _separator;
        protected virtual string Separator
        {
            get { return _separator ?? string.Empty; }
            set { _separator = value; }
        }

    }
}